{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# Grover from functional building blocks"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Setting the scene"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:50.633694Z",
     "iopub.status.busy": "2024-05-07T14:25:50.633452Z",
     "iopub.status.idle": "2024-05-07T14:25:50.636906Z",
     "shell.execute_reply": "2024-05-07T14:25:50.636321Z"
    }
   },
   "outputs": [],
   "source": [
    "# !pip install -U classiq"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:50.639318Z",
     "iopub.status.busy": "2024-05-07T14:25:50.638882Z",
     "iopub.status.idle": "2024-05-07T14:25:50.644139Z",
     "shell.execute_reply": "2024-05-07T14:25:50.643587Z"
    }
   },
   "outputs": [],
   "source": [
    "new_classiq_user = False\n",
    "if new_classiq_user:\n",
    "    import classiq\n",
    "\n",
    "    classiq.authenticate()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Warm Up"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### First Example"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Write a function that prepares the minus state $\\ket{-}=\\frac{1}{\\sqrt2}(\\ket{0}-\\ket{1})$, assuming it recives the qubit $\\ket{x}=\\ket{0}$ (hint: \n",
    "\n",
    "<details>\n",
    "<summary>\n",
    "HINT\n",
    "</summary>\n",
    "\n",
    "Use `H(x)`,`X(x)`\n",
    "</details>"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:50.646430Z",
     "iopub.status.busy": "2024-05-07T14:25:50.646258Z",
     "iopub.status.idle": "2024-05-07T14:25:53.724522Z",
     "shell.execute_reply": "2024-05-07T14:25:53.721649Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import H, QBit, X, qfunc\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def prepare_minus_state(x: QBit):\n",
    "    pass\n",
    "    # TODO complete here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we will test our code:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:53.727987Z",
     "iopub.status.busy": "2024-05-07T14:25:53.727324Z",
     "iopub.status.idle": "2024-05-07T14:25:53.731194Z",
     "shell.execute_reply": "2024-05-07T14:25:53.730622Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import Output, allocate\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(x: Output[QBit]):\n",
    "    allocate(1, x)\n",
    "    prepare_minus_state(x)  # Prepare the minus state"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:53.733511Z",
     "iopub.status.busy": "2024-05-07T14:25:53.733211Z",
     "iopub.status.idle": "2024-05-07T14:25:55.079198Z",
     "shell.execute_reply": "2024-05-07T14:25:55.078002Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import create_model, synthesize\n",
    "\n",
    "quantum_model = create_model(main)\n",
    "quantum_program = synthesize(quantum_model)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:55.084576Z",
     "iopub.status.busy": "2024-05-07T14:25:55.083330Z",
     "iopub.status.idle": "2024-05-07T14:25:55.185837Z",
     "shell.execute_reply": "2024-05-07T14:25:55.185178Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/1bb9494d-6aa4-48e2-9596-3f39debea774?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import show\n",
    "\n",
    "show(quantum_program)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some basic explanations about the high-level functional design with Classiq:\n",
    "\n",
    "* There should always be a main (`def main(...)`) function - the model that captures your algortihm is described there\n",
    "\n",
    "* The model is always generated out of the main function \n",
    "\n",
    "* The model is sent to the synthesis engine (compiler) that return a quantum program which contains the quantum circuit\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Some basic guidelines about the modeling language (QMOD):\n",
    "\n",
    "1. Every quantum variable should be declared, either as a parameter of a funciton e.g. `def prepare_minus(x: QBit)` or within the function itself with `x = QBit('x')`\n",
    "\n",
    "2. Some quantum variables need to be initalized with the `allocate` function. This is required in 2 cases:\n",
    "* A variable is a parameter of a function with the declaration `Output` like `def main(x: Output[QNum])`\n",
    "* A variable that was declared within a function like `a = QNum('a')`\n",
    "\n",
    "3. For the `main` function, you will always use `Output` for all variables, as the function does not receive any input"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Important tip!\n",
    "\n",
    "You can see all the declarations of the functions with what are their input arguments in the `functions.py` file within the classiq package (or by just right clicking a function and presing `Go To Defintion`)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Uniform Superposition"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Let's continue warming up with creating a function that receives a quantum register and creates a uniform superposition for all qubits within this array. You should use the function `apply_to_all(gate_operand=, target=)`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:55.233885Z",
     "iopub.status.busy": "2024-05-07T14:25:55.233579Z",
     "iopub.status.idle": "2024-05-07T14:25:55.237665Z",
     "shell.execute_reply": "2024-05-07T14:25:55.236916Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import QArray, apply_to_all\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def create_initial_state(reg: QArray[QBit]):\n",
    "    pass\n",
    "    # TODO complete here apply_to_all(gate_operand=, target=)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Test yout function by creating a new main function, synthesizing and viewing the circuit:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:55.242937Z",
     "iopub.status.busy": "2024-05-07T14:25:55.242563Z",
     "iopub.status.idle": "2024-05-07T14:25:55.246448Z",
     "shell.execute_reply": "2024-05-07T14:25:55.245807Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def main(x: Output[QArray[QBit]]):\n",
    "    allocate(7, x)\n",
    "    create_initial_state(x)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:55.249970Z",
     "iopub.status.busy": "2024-05-07T14:25:55.249754Z",
     "iopub.status.idle": "2024-05-07T14:25:57.448179Z",
     "shell.execute_reply": "2024-05-07T14:25:57.447485Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/6ee14d77-ba40-4733-9a2d-0740f4390b6a?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "model = create_model(main)\n",
    "qprog = synthesize(model)\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Function of a function"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In our Grover example we will have 3 variables `a,b,c`. We want to prepare all of them in an initial state of equal superposition. Create a fucntion that receives these 3 quantum variables as quantum integers (`QNum`) and applies the `create_inital_state` function to each:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 10,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.450810Z",
     "iopub.status.busy": "2024-05-07T14:25:57.450469Z",
     "iopub.status.idle": "2024-05-07T14:25:57.454097Z",
     "shell.execute_reply": "2024-05-07T14:25:57.453448Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import QNum\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def create_initial_states(a: QNum, b: QNum, c: QNum):\n",
    "    pass\n",
    "    # TODO Complete here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "You can create a main function, synthesize and visualize the generated circuit if you want to test yourself."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Oracle - Reflection around bad states"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Tehoretical Background"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Overall we can understand the Grover operator as composed of two reflection operators:\n",
    "1. Around the superposition of 'bad states' (i.e. not the solutions)\n",
    "2. Around the initial guess state\n",
    "\n",
    "In this section we will build the first reflection operator which is also the implementation of the oracle function. Geometrically it can be understood in the 2D vector space of $span\\{\\ket{\\psi_{good}},\\ket{\\psi_{bad}}\\}$."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![Oracle](https://docs.classiq.io/resources/grover_workhsop_reflection_bad_states.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The above figures describes geometrically the reflection of some state $\\ket{\\psi}=\\alpha\\ket{\\psi_{good}}+\\beta\\ket{\\psi_{bad}}$ around the state $\\ket{\\psi_{bad}}$ such that\n",
    "$\\begin{equation}\n",
    "R(\\alpha\\ket{\\psi_{good}}+\\beta\\ket{\\psi_{bad}}) = -\\alpha\\ket{\\psi_{good}}+\\beta\\ket{\\psi_{bad}}\n",
    "\\end{equation}$\n",
    "\n",
    "This operator can also be written as\n",
    "$\\begin{equation}\n",
    "R\\ket{x}=(-1)^{x==\\text{good solution}}\\ket{x}\n",
    "\\end{equation}$\n",
    "\n",
    "so if the state of $x$ is a solution it gets a $(-)$ phase."
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we will actually implement the black box, the oracle, people are speaking about in quantum algortihms. The beuty of Classiq is that we just need to specify its functionality, and Classiq automatically implements it for us.\n",
    "For our purposes, we want to find all the states that obey $2a+b=c$ so there are 3 quantum variables. In addition, we want to store our results somewhere, i.e. to indicate for each tupple of 3 nunmbers (a,b,c) (e.g. (1,2,2)) if the state is what we are looking for ($2*1+2=4!=2$ so the result is FALSE in this case). \n",
    "\n",
    "We will store the result in the variable `res` such that: $\\begin{equation} res = res \\oplus (2a+b==c) \\end{equation}$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "What we really want to implement here is graphically described as:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![res](https://docs.classiq.io/resources/grover_workshop_res_xor_scheme.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Adapt the following function so it will apply the desired equation: "
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.457052Z",
     "iopub.status.busy": "2024-05-07T14:25:57.456689Z",
     "iopub.status.idle": "2024-05-07T14:25:57.459889Z",
     "shell.execute_reply": "2024-05-07T14:25:57.459327Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def oracle_black_box(res: QNum, a: QNum, b: QNum, c: QNum):\n",
    "    # TODO Adapt with the correct statement\n",
    "    res ^= a + b + c == 8"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now let's go quantum! We want to store the result of the above operation in the phase of the state $\\ket{a,b,c}$. That is, we want $\\begin{equation} \\ket{a,b,c}\\rightarrow(-1)^{(2a+b==c)}\\ket{a,b,c}\\end{equation}$\n",
    "\n",
    "There is a common procedure in quantum algortihm that applies this and it is called phase kickback. It's working by applying the above `oracle_black_box` function to an initial `res` qubit to the state $\\ket{-}$ (has anyone prepared a function `prepare_minus(x)` by any chance?)\n",
    "\n",
    "You can work out the math that the following scheme imoplement what we want:"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![phase_kickback_scheme](https://docs.classiq.io/resources/grover_workshop_phase_kickback_scheme.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we can implement it using our oracle:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.462375Z",
     "iopub.status.busy": "2024-05-07T14:25:57.462031Z",
     "iopub.status.idle": "2024-05-07T14:25:57.468627Z",
     "shell.execute_reply": "2024-05-07T14:25:57.467997Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import free, invert\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def oracle_function(a: QNum, b: QNum, c: QNum):\n",
    "    aux = QBit(\"aux\")\n",
    "\n",
    "    allocate(1, aux)\n",
    "    prepare_minus_state(aux)\n",
    "\n",
    "    oracle_black_box(aux, a, b, c)\n",
    "\n",
    "    # We want to bring the aux state back to it's initial value and to free it up for further use\n",
    "    invert(lambda: prepare_minus_state(aux))\n",
    "    free(aux)  # and that it can be re-used"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Diffuser - Reflection around initial guess"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Theoretical Background"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "The second part of the Grover operator is the diffuser, which can be viewed as the reflection operator around our initial guess. "
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "![diffuser](https://docs.classiq.io/resources/grover_workshop_reflection_zero_state.png)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "As with the oracle reflection operator, we can describe any state $\\ket{psi}$ as a superposition of the initial state $\\ket{\\psi_0}$ such that  and the orthogoanl state to it $\\ket{\\psi_0^{\\bot}}$ \n",
    "\n",
    "$\\begin{equation}\n",
    "\\ket{\\psi} = \\alpha \\ket{\\psi_0} +\\beta \\ket{\\psi_0^{\\bot}}\n",
    "\\end{equation}$\n",
    "\n",
    "Here what we want to implement is to add a $(-)$ phase for all states that are not equal our initial guess, that is the reflection operator (our diffuser) is defined as:\n",
    "\n",
    "$\\begin{equation}\n",
    "R(\\alpha \\ket{\\psi_0} +\\beta \\ket{\\psi_0^{\\bot}}) = \\alpha \\ket{\\psi_0} -\\beta \\ket{\\psi_0^{\\bot}}\n",
    "\\end{equation}$\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "In order to implement the reflection around our initial state, we will implement a reflection around the zero state $\\ket{0}$, and then squiz it between to state preperations operators for our initial state $\\ket{\\psi_0}$. That is, if $U_{\\psi_0}\\ket{0}=\\ket{\\psi_0}$ then we will implement the desired $R$ operator with:\n",
    "$\\begin{equation}\n",
    "R = U_{\\psi_0}R_0 U_{\\psi_0}^{\\dagger}\n",
    "\\end{equation}$\n",
    "\n",
    "where $R_0$ is the reflection operator around the zero state:\n",
    "$\\begin{equation}\n",
    "R_0\\ket{x} = (-1)^{x\\ne0}\\ket{x}= (2\\ket{0}\\bra{0}-I)\\ket{x}\n",
    "\\end{equation}$"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "### Implementation"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "First we will implement the not equal zero function which takes `aux` and `x` as inputs and applies \n",
    "$\\begin{equation}\n",
    "res = res \\oplus (x\\ne0)\n",
    "\\end{equation}$"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.471420Z",
     "iopub.status.busy": "2024-05-07T14:25:57.470969Z",
     "iopub.status.idle": "2024-05-07T14:25:57.474053Z",
     "shell.execute_reply": "2024-05-07T14:25:57.473455Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def not_equal_zero(aux: QBit, x: QNum):\n",
    "    aux ^= x == 0\n",
    "    X(aux)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now we will use the common trick of phase kick back again. As the `aux` qubit for the `not_equal_zero` funciton we need to insert the $\\ket{-}$ state after it has been initialized and apploied, and after the application of the function we need to return `aux` to its initial state and to free it up (We did this precedure before).\n",
    "\n",
    "<details>\n",
    "<summary>\n",
    "HINT\n",
    "</summary>\n",
    "\n",
    "1. declare the auxilary qubit\n",
    "2. initalize it using `allocate`\n",
    "3. prepare the $\\ket{-}$ state\n",
    "4. Apply the `not_equal_zero` funciton\n",
    "5. reverse the $\\ket{-}$ with the `invert` operation\n",
    "6. free the auxilary qubit\n",
    "\n",
    "</details>\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.476600Z",
     "iopub.status.busy": "2024-05-07T14:25:57.475946Z",
     "iopub.status.idle": "2024-05-07T14:25:57.479631Z",
     "shell.execute_reply": "2024-05-07T14:25:57.479063Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def zero_diffuser(x: QNum):\n",
    "    pass\n",
    "    # TODO complete here"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now after we've implemented the zero diffuser, we need to sandwitch it with the state preperations of our initial state `a,b,c`. The tricky part here is that the zero diffuser expects to receive only 1 quantunm variable `x` but we have three. So what should we do? We combine them to one quantum variable with the `bind` operation, treating them as one variable, and then splitting them back into 3 variables with the `bind` operation again."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 15,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.481992Z",
     "iopub.status.busy": "2024-05-07T14:25:57.481557Z",
     "iopub.status.idle": "2024-05-07T14:25:57.485523Z",
     "shell.execute_reply": "2024-05-07T14:25:57.484865Z"
    }
   },
   "outputs": [],
   "source": [
    "from classiq import bind\n",
    "\n",
    "size_a = 2\n",
    "size_b = 2\n",
    "size_c = 3\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def initial_state_diffuser(a: QNum, b: QNum, c: QNum):\n",
    "\n",
    "    create_initial_states(a, b, c)\n",
    "\n",
    "    abc = QNum(\"abc\")\n",
    "    bind([a, b, c], abc)\n",
    "    zero_diffuser(abc)\n",
    "    bind(abc, [a, b, c])\n",
    "    invert(lambda: create_initial_states(a, b, c))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## Putting all together"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "That's it! You've made it! Now is the time to harvest the fruits of the hard work and put everything together!\n",
    "Complete your grover operator by implementing the two functions that you've built, first the `oracle_fucntion` and then the `inital_state_diffuser`:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 16,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.489349Z",
     "iopub.status.busy": "2024-05-07T14:25:57.489156Z",
     "iopub.status.idle": "2024-05-07T14:25:57.493033Z",
     "shell.execute_reply": "2024-05-07T14:25:57.492561Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def my_grover_operator(a: QNum, b: QNum, c: QNum):\n",
    "    # TODO complete here\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Now that we have our Grover operator, we can run it within our code. We have 3 steps here:\n",
    "1. initalize `a`,`b` and `c` within the scope of the `main` function using the `allocate` operation\n",
    "2. create the initial states for `a`,`b` and `c` \n",
    "3. Apply your Grover operator"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 17,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.495590Z",
     "iopub.status.busy": "2024-05-07T14:25:57.495412Z",
     "iopub.status.idle": "2024-05-07T14:25:57.499593Z",
     "shell.execute_reply": "2024-05-07T14:25:57.499104Z"
    }
   },
   "outputs": [],
   "source": [
    "@qfunc\n",
    "def main(a: Output[QNum], b: Output[QNum], c: Output[QNum]):\n",
    "    allocate(size_a, a)\n",
    "    allocate(size_b, b)\n",
    "    allocate(size_c, c)\n",
    "\n",
    "    # TODO complete here\n",
    "    pass"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Synthesize your model:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 18,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:57.502331Z",
     "iopub.status.busy": "2024-05-07T14:25:57.501985Z",
     "iopub.status.idle": "2024-05-07T14:25:59.661941Z",
     "shell.execute_reply": "2024-05-07T14:25:59.661213Z"
    }
   },
   "outputs": [],
   "source": [
    "qmod = create_model(main)\n",
    "qprog = synthesize(qmod)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "And view it within the IDE:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 19,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:59.678040Z",
     "iopub.status.busy": "2024-05-07T14:25:59.675996Z",
     "iopub.status.idle": "2024-05-07T14:25:59.794381Z",
     "shell.execute_reply": "2024-05-07T14:25:59.792867Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/40c8f298-1e9e-4d9d-8c45-bb2d127444d6?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Is it what you were expecting?\n",
    "Now we can play with the constraints as we did in the IDE:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 20,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:25:59.798972Z",
     "iopub.status.busy": "2024-05-07T14:25:59.797744Z",
     "iopub.status.idle": "2024-05-07T14:26:00.994828Z",
     "shell.execute_reply": "2024-05-07T14:26:00.994120Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/7916e73f-7c7c-4a75-bea7-915dd9438baa?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import Constraints, set_constraints\n",
    "\n",
    "qmod = set_constraints(qmod, Constraints(optimization_parameter=\"depth\"))  # or 'width'\n",
    "qprog = synthesize(qmod)\n",
    "show(qprog)"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "CONGRADULATIONS!\n",
    "\n",
    "You have completed your own Grover algortihm implementation from functional building blocks without swegging under the rug any details, really impressive work!"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "#### The full solution for your reference"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 21,
   "metadata": {
    "execution": {
     "iopub.execute_input": "2024-05-07T14:26:00.997565Z",
     "iopub.status.busy": "2024-05-07T14:26:00.997185Z",
     "iopub.status.idle": "2024-05-07T14:26:08.641198Z",
     "shell.execute_reply": "2024-05-07T14:26:08.640490Z"
    }
   },
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/7be18940-1e20-488e-88cb-7e66a2da8bef?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Opening: https://platform.classiq.io/circuit/400603b7-92d0-4e5a-be8e-f9aef5897a1a?version=0.41.0.dev39%2B79c8fd0855\n"
     ]
    }
   ],
   "source": [
    "from classiq import (\n",
    "    Constraints,\n",
    "    H,\n",
    "    Output,\n",
    "    QArray,\n",
    "    QBit,\n",
    "    QNum,\n",
    "    X,\n",
    "    allocate,\n",
    "    apply_to_all,\n",
    "    bind,\n",
    "    create_model,\n",
    "    free,\n",
    "    invert,\n",
    "    qfunc,\n",
    "    set_constraints,\n",
    "    show,\n",
    "    synthesize,\n",
    "    write_qmod,\n",
    ")\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def prepare_minus_state(x: QBit):\n",
    "    X(x)\n",
    "    H(x)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def create_initial_state(reg: QArray[QBit]):\n",
    "    apply_to_all(gate_operand=H, target=reg)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def create_initial_state(reg: QArray[QBit]):\n",
    "    apply_to_all(lambda qb: H(qb), reg)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def create_initial_states(a: QNum, b: QNum, c: QNum):\n",
    "    create_initial_state(a)\n",
    "    create_initial_state(b)\n",
    "    create_initial_state(c)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def oracle_black_box(res: QNum, a: QNum, b: QNum, c: QNum):\n",
    "    res ^= 2 * a + b == c\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def oracle_function(a: QNum, b: QNum, c: QNum):\n",
    "    aux = QBit(\"aux\")\n",
    "\n",
    "    allocate(1, aux)\n",
    "    prepare_minus_state(aux)\n",
    "\n",
    "    oracle_black_box(aux, a, b, c)\n",
    "\n",
    "    # We want to bring the aux state back to it's initial value and to free it up for further use\n",
    "    invert(lambda: prepare_minus_state(aux))\n",
    "    free(aux)  # and that it can be re-used\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def not_equal_zero(aux: QBit, x: QNum):\n",
    "    aux ^= x == 0\n",
    "    X(aux)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def zero_diffuser(x: QNum):\n",
    "    aux = QBit(\"aux\")\n",
    "    allocate(1, aux)\n",
    "\n",
    "    prepare_minus_state(aux)\n",
    "    not_equal_zero(aux, x)\n",
    "    invert(lambda: prepare_minus_state(aux))\n",
    "    free(aux)\n",
    "\n",
    "\n",
    "size_a = 2\n",
    "size_b = 2\n",
    "size_c = 3\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def initial_state_diffuser(a: QNum, b: QNum, c: QNum):\n",
    "\n",
    "    create_initial_states(a, b, c)\n",
    "\n",
    "    abc = QNum(\"abc\", size=size_a + size_b + size_c, fraction_digits=0, is_signed=False)\n",
    "    bind([a, b, c], abc)\n",
    "    zero_diffuser(abc)\n",
    "    bind(abc, [a, b, c])\n",
    "    invert(lambda: create_initial_states(a, b, c))\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def my_grover_operator(a: QNum, b: QNum, c: QNum):\n",
    "    oracle_function(a, b, c)\n",
    "    initial_state_diffuser(a, b, c)\n",
    "\n",
    "\n",
    "@qfunc\n",
    "def main(a: Output[QNum], b: Output[QNum], c: Output[QNum]):\n",
    "    allocate(size_a, a)\n",
    "    allocate(size_b, b)\n",
    "    allocate(size_c, c)\n",
    "    create_initial_states(a, b, c)\n",
    "    my_grover_operator(a, b, c)\n",
    "\n",
    "\n",
    "qmod = create_model(main)\n",
    "qprog = synthesize(qmod)\n",
    "show(qprog)\n",
    "\n",
    "qmod = set_constraints(qmod, Constraints(optimization_parameter=\"depth\"))  # or 'width'\n",
    "qprog = synthesize(qmod)\n",
    "show(qprog)\n",
    "\n",
    "write_qmod(qmod, \"grover_workshop\")"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.11.9"
  },
  "vscode": {
   "interpreter": {
    "hash": "529b62266d4f537a408698cf820854c65fe877011c7661f0f70aa11c4383fddc"
   }
  }
 },
 "nbformat": 4,
 "nbformat_minor": 4
}
